The compile() and link() Functions

Course- AngularJS >

If you need to do something more advanced inside your directive, something that you cannot do with an HTML template, you can use the compile() and link() functions instead.

The compile() and link() functions define how the directive is to modify the HTML that matched the directive.

The compile() function is called once for each occurrence of the directive in the HTML page. The compile() function can then do any one-time configuration needed of the element containing the directive.

The compile() function finishes by returning the link() function. The link() function is called every time the element is to be bound to data in the $scope object.

As mentioned, you add the compile() function to the directive definition object, and the compile() function has to return the link() function when executed. Here is how that looks:

<script>
myapp = angular.module("myapp", []);
myapp.directive('userinfo', function() {
    var directive = {};

    directive.restrict = 'E'; /* restrict this directive to elements */


    directive.compile = function(element, attributes) {
        // do one-time configuration of element.

        var linkFunction = function($scope, element, atttributes) {
            // bind element to data in $scope
        }

        return linkFunction;
    }

    return directive;
});    
</script>    

The compile() function takes two parameters: The element and attributes parameters.

The element parameter is a jqLite wrapped DOM element. AngularJS contains a lite version of jQuery to help you do DOM manipulation, so the element's DOM manipulation methods are the same as you know from jQuery.

The attributes parameter is a JavaScript object containing properties for all the attributes of the DOM element. Thus, to access an attribute named type you would write attributes.type.

The link() function takes three parameters: The $scope parameter, the element parameter and the attributes parameter. The element and attributes parameter is the same as passed to the compile() function. The $scope parameter is the normal scope object, or an isolate scope in case you have specified one in the directive definition object.

The compile() and link() function names are actually confusing. They are inspired by compiler terms. I can see the resemblance, but a compiler parses an input once, and creates an output. A directive configures an HTML element and then updates that HTML subsequently whenever the $scope object changes.

A better name for the compile() function would have been something like create(), init() or configure(). Something that signals that this function is only called once.

A better name for the link() function would have been something like bind() or render(), which signals that this function is called whenever the directive needs to bind data to it, or to re-render it.

Here is a full example that shows a directive that uses both a compile() and link() function:

<div ng-controller="MyController" >
    <userinfo >This will be replaced</userinfo>
</div>

<script>
    myapp = angular.module("myapp", []);
    myapp.directive('userinfo', function() {
        var directive = {};

        directive.restrict = 'E'; /* restrict this directive to elements */

        directive.compile = function(element, attributes) {
            element.css("border", "1px solid #cccccc");

            var linkFunction = function($scope, element, attributes) {
                element.html("This is the new content: " + $scope.firstName);
                element.css("background-color", "#ffff00");
            }

            return linkFunction;
        }

        return directive;
    })
    myapp.controller("MyController", function($scope, $http) {
        $scope.cssClass = "notificationDiv";

        $scope.firstName = "Jakob";

        $scope.doClick = function() {
            console.log("doClick() called");
        }
    });
</script>    

The compile() function sets a border on the HTML element. This is only executed once because the compile() function is only executed once.

The link() function replaces the content of the HTML element, and sets the background color to yellow.

There is no particular reason why the border was set in the compile() function, and the background color in the link() function. Both could have been set in the compile() function, or both in the link() function. If set in the compile() function they would only have been set once (which is often what you want). If set in the link() function they would be set every time the HTML element is bound to data in the $scope object. This might be useful if you needed to set the border and background color differently depending on data in the $scope object.

Setting Only a link() Function

Sometimes you do not need the compile() step for your directive. You only need th link() function. In that case you can set the link() function directly on the directive definition object. Here is the example from before, with only a link function:

<div ng-controller="MyController" >
    <userinfo >This will be replaced</userinfo>
</div>

<script>
    myapp = angular.module("myapp", []);
    myapp.directive('userinfo', function() {
        var directive = {};

        directive.restrict = 'E'; /* restrict this directive to elements */

        directive.link = function($scope, element, attributes) {
                element.html("This is the new content: " + $scope.firstName);
                element.css("background-color", "#ffff00");
        }

        return directive;
    })
    myapp.controller("MyController", function($scope, $http) {
        $scope.cssClass = "notificationDiv";

        $scope.firstName = "Jakob";

        $scope.doClick = function() {
            console.log("doClick() called");
        }
    });
</script>

Notice how the link() function does the same as the link() function returned in the previous example.